/*
 * 程序名：demo6.cpp，此程序用于演示socket通信的服务端
 */

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
using namespace std;

int main(int argc,char *argv[])
{
	if (argc!=2)
	{
		cout << "Using:./demo6 通讯端口\nExample:./demo6 5005\n\n"; // 端口大于1024，不与其它的重复。
		cout << "注意:运行服务端程序的Linux系统的防火墙必须要开通5005端口。\n";
		cout << " 如果是云服务器，还要开通云平台的访问策略。\n\n";
		return -1;
	}
	
	// 第1步：创建服务端的socket。
	int listenfd = socket(AF_INET,SOCK_STREAM,0);
	if (listenfd==-1)
	{
		perror("socket"); return -1;
	}

	// 第2步：把服务端用于通信的IP和端口绑定到socket上。
	struct sockaddr_in servaddr; // 用于存放协议、端口和IP地址的结构体。
	memset(&servaddr,0,sizeof(servaddr));
	servaddr.sin_family=AF_INET; // ①协议族，固定填AF_INET。
	servaddr.sin_port=htons(atoi(argv[1])); // ②指定服务端的通信端口。
	servaddr.sin_addr.s_addr=htonl(INADDR_ANY); // ③如果操作系统有多个IP，全部的IP都可以用于通讯。
	//servaddr.sin_addr.s_addr=inet_addr("192.168.101.138"); // ③指定服务端用于通讯的IP(大端序)。
	// 绑定服务端的IP和端口。
	if (bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr))==-1)
	{
		perror("bind"); close(listenfd); return -1;
	}

	// 第3步：把socket设置为可连接（监听）的状态。
	if (listen(listenfd,5) == -1 )
	{
		perror("listen"); close(listenfd); return -1;
	}

	// 第4步：受理客户端的连接请求，如果没有客户端连上来，accept()函数将阻塞等待。
	int clientfd=accept(listenfd,0,0);
	if (clientfd==-1)
	{
		perror("accept"); close(listenfd); return -1;
	}
	cout << "客户端已连接。\n";
	
	// 第5步：与客户端通信，接收客户端发过来的报文后，回复ok。
	char buffer[1024];
	while (true)
	{
		int iret;
		memset(buffer,0,sizeof(buffer));
		// 接收客户端的请求报文，如果客户端没有发送请求报文，recv()函数将阻塞等待。
		// 如果客户端已断开连接，recv()函数将返回0。
		if ( (iret=recv(clientfd,buffer,sizeof(buffer),0))<=0)
		{
			cout << "iret=" << iret << endl; break;
		}
		cout << "接收：" << buffer << endl;
		strcpy(buffer,"ok"); // 生成回应报文内容。
		// 向客户端发送回应报文。
		if ( (iret=send(clientfd,buffer,strlen(buffer),0))<=0)
		{
			perror("send"); break;
		}
		cout << "发送：" << buffer << endl;
	}

	// 第6步：关闭socket，释放资源。
	close(listenfd); // 关闭服务端用于监听的socket。
	close(clientfd); // 关闭客户端连上来的socket。

	return 0;
}




